home *** CD-ROM | disk | FTP | other *** search
- #include <exec/types.h>
-
- #ifdef __SASC
- #include <dos.h>
- #endif
-
- #include "defs.h"
-
- static char rcsid [] = "$Id: prepager.c,v 3.5 95/12/16 18:36:46 Martin_Apel Exp $";
-
- /* The PrePager has two different tasks:
- *
- * 1. Free memory which should be freed by other tasks from inside a Forbid
- * / Disable. A descriptor of the memory to be freed is put into a queue
- * and freed by the prepager the time Permit / Enable is called.
- *
- * 2. Prevent IO from or to the paging device using virtual memory which is
- * possibly not locked into memory. Otherwise a deadlock would result.
- */
-
- PRIVATE struct IOStdReq *CopiedReq;
- PRIVATE struct DosPacket *CopiedPacket;
- PRIVATE struct MsgPort *FileHandlerPort;
- PRIVATE struct Message* (*OrigPktWait) (void);
- PRIVATE struct VMMsg *PrePageMsg;
- PRIVATE UWORD ResetSignal;
- PRIVATE struct Message QuitMsg;
- PRIVATE void *ResetHandlerData;
-
- #define MIN(a,b) ((a)<(b)?(a):(b))
-
- /********************************************************************/
-
- PRIVATE void FreeForbiddenMem (void)
-
- {
- struct ForbiddenFreeStruct *tmp_ffs;
- void *free_start;
- ULONG free_size;
-
- while (VMToBeFreed != NULL)
- {
- Forbid ();
- tmp_ffs = VMToBeFreed;
- free_start = tmp_ffs->address;
- free_size = tmp_ffs->size;
- VMToBeFreed = tmp_ffs->NextFree;
- tmp_ffs->NextFree = VMFreeRecycling;
- VMFreeRecycling = tmp_ffs;
- Permit ();
- PRINT_DEB ("Freeing memory for other task", 0L);
- FreeMem (free_start, free_size);
- }
- }
-
- /********************************************************************/
-
- PRIVATE void PrepageIOStdReq (struct IOStdReq *req)
-
- {
- ULONG Offset;
- BYTE Error;
- ULONG Actual;
- ULONG From;
- void *buffer;
- ULONG size;
-
- PRINT_DEB ("PrepageIOStdReq called", 0L);
-
- From = (ULONG)req->io_Data;
-
- PRINT_DEB ("Current number of pageframes: %ld", NumPageFrames);
- PRINT_DEB ("Length of current request: %lx", req->io_Length);
-
- Offset = req->io_Offset;
- Actual = 0L;
- Error = FALSE;
-
- while (From < (ULONG)req->io_Data + req->io_Length)
- {
- /* Bug fix: If there are multiple units in the paging device, the
- * Unit pointer must not be taken from OpenDevice, but be copied from
- * the received request.
- */
- CopiedReq->io_Device = req->io_Device;
- CopiedReq->io_Unit = req->io_Unit;
-
- size = (ULONG)req->io_Data + req->io_Length - From;
- while ((buffer = AllocVec (size, MEMF_PUBLIC)) == NULL)
- {
- PRINT_DEB ("Request has to be split", 0L);
- size = ALIGN_DOWN (size / 2, PagingDevParams.block_size);
- if (size == 0)
- {
- PRINT_DEB ("Not enough memory to copy one block of data", 0L);
- req->io_Error = IOERR_BADADDRESS; /* This is not what it should */
- req->io_Actual = 0; /* be, but there's no better */
- /* error code available */
- ReplyMsg ((struct Message*)req);
- return;
- }
- }
-
- CopiedReq->io_Command = req->io_Command;
- /* the quick bit has been cleared by the parthandler */
- CopiedReq->io_Flags = req->io_Flags | IOF_QUICK;
- CopiedReq->io_Data = buffer;
- CopiedReq->io_Offset = Offset;
- CopiedReq->io_Length = size;
-
- if (req->io_Command == CMD_WRITE)
- {
- if ((From & 0x3) == 0) /* longword-aligned */
- CopyMemQuick ((APTR)From, buffer, size);
- else
- CopyMem ((APTR)From, buffer, size);
- }
-
- PRINT_DEB ("Sending request to device", 0L);
- SendIO ((struct IORequest*)CopiedReq);
- WaitIO ((struct IORequest*)CopiedReq);
- PRINT_DEB ("Request finished", 0L);
-
- if (req->io_Command == CMD_READ)
- {
- if ((From & 0x3) == 0) /* longword-aligned */
- CopyMemQuick (buffer, (APTR)From, size);
- else
- CopyMem (buffer, (APTR)From, size);
- }
-
- FreeVec (buffer);
- Offset += size;
- From += size;
- Actual += CopiedReq->io_Actual;
-
- if (CopiedReq->io_Error)
- {
- Error = CopiedReq->io_Error;
- PRINT_DEB ("IOStdReq returned with error %ld", (ULONG)Error);
- PRINT_DEB ("Command was %ld", (ULONG)CopiedReq->io_Command);
- break;
- }
- }
-
- PRINT_DEB ("Done IO for requesting task", 0L);
-
- req->io_Error = Error;
- req->io_Actual= Actual;
-
- ReplyMsg ((struct Message*)req);
- }
-
- /********************************************************************/
-
- PRIVATE struct Message *GetPacket (void)
-
- {
- struct Message *msg;
-
- if (OrigPktWait != NULL)
- msg = (*OrigPktWait) ();
- else
- {
- while ((msg = GetMsg (FileHandlerPort)) == NULL)
- Wait (SIGF_DOS);
- }
-
- return (msg);
- }
-
- /********************************************************************/
-
- PRIVATE void PrepagePacket (struct DosPacket *packet);
-
- PRIVATE struct Message *PktWaitPatch (void)
-
- {
- struct Message *received_msg;
- struct DosPacket *received_packet;
-
- for (;;)
- {
- received_msg = GetPacket ();
- if (received_msg == &QuitMsg)
- {
- /* This is the quit msg. Deinstall myself */
- PRINT_DEB ("Deinstalling PktWaitPatch", 0L);
- FileHandlerProcess->pr_PktWait = (APTR)OrigPktWait;
- return (NULL);
- }
-
- received_packet = (struct DosPacket*)received_msg->mn_Node.ln_Name;
-
- switch (received_packet->dp_Action)
- {
- case ACTION_READ:
- case ACTION_WRITE:
- if ((received_packet->dp_Arg2 >= VirtAddrStart) &&
- (received_packet->dp_Arg2 < VirtAddrEnd))
- {
- PRINT_DEB ("Received R/W packet to VM", 0L);
- PutMsg (PrePagerPort, received_msg);
- break;
- }
- default:
- return (received_msg);
- }
- }
- }
-
- /********************************************************************/
-
- PRIVATE void PrepagePacket (struct DosPacket *packet)
-
- {
- long Actual;
- long result;
- struct MsgPort *ReturnPort;
- ULONG From,
- size;
- void *buffer;
-
- /* dp_Arg1 contains pointer to Object
- * dp_Arg2 is start of transfer
- * dp_Arg3 is length of transfer
- */
-
- PRINT_DEB ("PrepagePacket called.", 0L);
- From = packet->dp_Arg2;
- Actual = 0;
-
- while (From < packet->dp_Arg2 + packet->dp_Arg3)
- {
- size = (ULONG)packet->dp_Arg2 + packet->dp_Arg3 - From;
- PRINT_DEB ("Allocating %ld bytes for tmp buffer", size);
- while ((buffer = AllocVec (size, MEMF_PUBLIC)) == NULL)
- {
- PRINT_DEB ("Packet has to be split", 0L);
- size = size / 2;
- if (size == 0)
- {
- PRINT_DEB ("Not enough memory to copy data", 0L);
- packet->dp_Res1 = -1; /* Error marker */
- packet->dp_Res2 = ERROR_NO_FREE_STORE;
- ReturnPort = packet->dp_Port;
- packet->dp_Port = FileHandlerPort;
- PutMsg (ReturnPort, packet->dp_Link);
- return;
- }
- }
-
- if (packet->dp_Action == ACTION_WRITE)
- CopyMem ((APTR)From, buffer, size);
-
- CopiedPacket->dp_Action = packet->dp_Action;
- CopiedPacket->dp_Arg1 = packet->dp_Arg1;
- CopiedPacket->dp_Arg2 = (LONG)buffer;
- CopiedPacket->dp_Arg3 = size;
-
- SendPkt (CopiedPacket, FileHandlerPort, &(PrePagerProcess->pr_MsgPort));
- WaitPkt ();
- result = CopiedPacket->dp_Res1;
-
- if (packet->dp_Action == ACTION_READ)
- CopyMem (buffer, (APTR)From, result); /* result contains the number */
- /* of bytes read */
-
- FreeVec (buffer);
-
- From += size;
- Actual += result;
-
- if (result <= 0)
- break;
- }
-
- PRINT_DEB ("Done IO for requesting task", 0L);
-
- packet->dp_Res1 = Actual;
- packet->dp_Res2 = CopiedPacket->dp_Res2;
- ReturnPort = packet->dp_Port;
- packet->dp_Port = FileHandlerPort;
- PutMsg (ReturnPort, packet->dp_Link);
- }
-
- /*********************************************************************/
-
- PRIVATE void ResetHandler (void)
-
- {
- PRINT_DEB ("Sending reset signal to prepager", 0L);
- Signal (PrePagerTask, 1L << ResetSignal);
- }
-
- /********************************************************************/
-
- PRIVATE int InitPrePager (void)
-
- {
- /* 16 signals per task are guaranteed, so the following four lines
- * can't fail
- */
-
- PrePagerQuitSignal = AllocSignal (-1L);
- FreeVMSignal = AllocSignal (-1L);
- ResetSignal = AllocSignal (-1L);
-
- if ((PrePagerPort = CreateMsgPort ()) == NULL)
- return (ERR_NOT_ENOUGH_MEM);
-
- if ((CopiedReq = CreateIORequest (PrePagerPort, sizeof (struct IOStdReq)))
- == NULL)
- return (ERR_NOT_ENOUGH_MEM);
-
- PRINT_DEB ("Patching BeginIO", 0L);
-
- OrigBeginIO = (void (*) ()) SetFunction ((struct Library*)PagingDevParams.device,
- DEV_BEGINIO, (ULONG (*) ()) PartHandler);
-
- PRINT_DEB ("BeginIO successfully patched", 0L);
-
- VMToBeFreed = NULL;
- VMFreeRecycling = NULL;
-
- if ((PrePageMsg = DoOrigAllocMem (sizeof (struct VMMsg), MEMF_PUBLIC)) == NULL)
- return (ERR_NOT_ENOUGH_MEM);
-
- PRINT_DEB ("Allocated PrePageMsg", 0L);
-
- if (CurrentConfig.PageDev == PD_FILE)
- {
- if ((CopiedPacket = AllocDosObject (DOS_STDPKT, NULL)) == NULL)
- {
- PRINT_DEB ("No mem for CopiedPacket", 0L);
- return (ERR_NOT_ENOUGH_MEM);
- }
-
- FileHandlerProcess = (struct Process*)PagingDevParams.SysTask;
- FileHandlerPort = &(FileHandlerProcess->pr_MsgPort);
-
- /* Install PktWaitPatch */
- OrigPktWait = (struct Message* (*) ())FileHandlerProcess->pr_PktWait;
- FileHandlerProcess->pr_PktWait = (APTR)PktWaitPatch;
- }
-
- if ((ResetHandlerData = InstallResetHandler (ResetHandler, -32L)) == NULL)
- PRINT_DEB ("Couldn't install reset handler", 0L);
-
- PRINT_DEB ("Installed reset handler", 0L);
- return (SUCCESS);
- }
-
- /********************************************************************/
-
- PRIVATE void Cleanup_PrePager (void)
-
- {
- struct ForbiddenFreeStruct *tmp_ffs;
-
- RemoveResetHandler (ResetHandlerData);
-
- if (CurrentConfig.PageDev == PD_FILE)
- {
- PRINT_DEB ("Requesting PktWaitPatch to quit", 0L);
- PutMsg (FileHandlerPort, &QuitMsg);
- while (FileHandlerProcess->pr_PktWait != (APTR)OrigPktWait)
- Delay (1L);
-
- FreeDosObject (DOS_STDPKT, CopiedPacket);
- }
-
- if (PrePageMsg != NULL)
- FreeMem (PrePageMsg, sizeof (struct VMMsg));
-
- while ((tmp_ffs = VMFreeRecycling) != NULL)
- {
- VMFreeRecycling = tmp_ffs->NextFree;
- FreeMem (tmp_ffs, sizeof (struct ForbiddenFreeStruct));
- }
-
- if (OrigBeginIO != NULL)
- SetFunction ((struct Library*)PagingDevParams.device,
- DEV_BEGINIO, (ULONG (*) ()) OrigBeginIO);
-
- if (CopiedReq != NULL)
- DeleteIORequest ((struct IORequest*)CopiedReq);
-
- if (PrePagerPort != NULL)
- DeleteMsgPort (PrePagerPort);
-
- FreeSignal ((ULONG) PrePagerQuitSignal);
- FreeSignal ((ULONG) FreeVMSignal);
- FreeSignal ((ULONG) ResetSignal);
- }
-
- /********************************************************************/
-
- void PrePager (void)
-
- {
- ULONG WaitMask;
- ULONG ReceivedSignals;
- BOOL quit = FALSE;
- struct VMMsg *InitMsg;
- struct Message *PrePageMsg;
- int rc;
-
- if ((rc = InitPrePager ()) != SUCCESS)
- {
- PRINT_DEB ("Init failed", 0L);
- InitError (rc);
- Cleanup_PrePager ();
- Signal ((struct Task*)VM_ManagerProcess, 1L << InitPort->mp_SigBit);
- return;
- }
-
- PRINT_DEB ("Init successful", 0L);
- /* Tell VM_Manager that the PrePager has been initialized correctly */
- if ((InitMsg = DoOrigAllocMem (sizeof (struct VMMsg), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
- {
- PRINT_DEB ("PrePager: Not enough memory for init msg", 0L);
- InitError (ERR_NOT_ENOUGH_MEM);
- Cleanup_PrePager ();
- Signal ((struct Task*)VM_ManagerProcess, 1L << InitPort->mp_SigBit);
- return;
- }
-
- InitMsg->VMSender = FindTask (NULL);
- InitMsg->VMCommand = VMCMD_InitReady;
- InitMsg->ReplySignal = NULL; /* Let VM_Manager free this */
- PutMsg (InitPort, (struct Message*)InitMsg);
-
- WaitMask = (1L << PrePagerPort->mp_SigBit) |
- (1L << PrePagerQuitSignal) |
- (1L << FreeVMSignal) |
- (1L << ResetSignal);
-
- while (!quit)
- {
- PRINT_DEB ("Waiting for signals", 0L);
- ReceivedSignals = Wait (WaitMask);
- PRINT_DEB ("Received signals: %lx", ReceivedSignals);
-
- if (ReceivedSignals & (1L << PrePagerPort->mp_SigBit))
- {
- while ((PrePageMsg = GetMsg (PrePagerPort)) != NULL)
- {
- if ((TypeOfMem (PrePageMsg->mn_Node.ln_Name) != 0) &&
- (((struct DosPacket*)(PrePageMsg->mn_Node.ln_Name))->dp_Link ==
- PrePageMsg))
- PrepagePacket ((struct DosPacket*)PrePageMsg->mn_Node.ln_Name);
- else
- PrepageIOStdReq ((struct IOStdReq*)PrePageMsg);
- }
- }
-
- if (ReceivedSignals & (1L << FreeVMSignal))
- FreeForbiddenMem ();
-
- if (ReceivedSignals & (1L << PrePagerQuitSignal))
- quit = TRUE;
-
- if (ReceivedSignals & (1L << ResetSignal))
- {
- PRINT_DEB ("Received reset signal", 0L);
- ResetHandlerDone (ResetHandlerData);
- WaitMask = (1L << FreeVMSignal);
- }
- }
-
- Cleanup_PrePager ();
- PRINT_DEB ("Exiting", 0L);
- }
-